#ifndef __UTIL_HPP__
#define __UTIL_HPP__

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <atomic>
#include <ctime>
#include <cstring>
#include <cstdlib>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>

namespace yx
{
    class TimeUtil
    {
    public:
        // 获取日期时间
        static std::string getDateTime()
        {
            // 获取时间戳
            time_t ticks = time(NULL);
            struct tm *ptm = localtime(&ticks);
            // 格式化输出日期时间
            char datetime[64];
            memset(datetime, 0, sizeof(datetime));
            strftime(datetime, sizeof(datetime), "[%Y-%m-%d %H:%M:%S]", ptm);

            return std::string(datetime);
        }

        // 获取毫秒时间戳
        static std::string getTimeStampMs()
        {
            struct timeval tv;
            gettimeofday(&tv, nullptr);

            return std::to_string(tv.tv_sec * 1000 + tv.tv_usec / 1000);
        }
    };

    class FileUtil
    {
    public:
        // 判断指定文件是否存在
        static bool isFileExist(const std::string &path_name)
        {
            struct stat st;
            if (0 == stat(path_name.c_str(), &st))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        static bool unlinkFile(const std::string &fname)
        {
            return 0 == unlink(fname.c_str());
        }

        // 获取唯一文件名
        static std::string uniqueFileName()
        {
            std::atomic<unsigned long long> num(0);

            return std::to_string(num++) + "-" + TimeUtil::getTimeStampMs();
        }

        // 写文件
        static bool writeFile(const std::string &target_file, const std::string &content)
        {
            std::ofstream fout(target_file);
            if (!fout.is_open())
            {
                return false;
            }

            fout.write(content.c_str(), content.size());

            fout.close();
            return true;
        }

        // 读文件
        static bool readFile(const std::string &target_file, std::string &content)
        {
            std::ifstream fin(target_file);
            if (!fin.is_open())
            {
                return false;
            }
            // 1. 清空字符串
            content.clear();
            // 2. 一行一行的读
            std::string line;
            while (std::getline(fin, line))
            {
                content.append(line);
                // 3. 保持文件的原样
                content.push_back('\n');
            }
            fin.close();
            return true;
        }
    };

    class PathUtil
    {
        static const std::string tempdir_path;

    public:
        // xxx-> ./tempdir/xxx.suffix
        static std::string addSuffix(const std::string &file_name, const std::string &suffix)
        {
            std::string res = tempdir_path;
            res += file_name;
            res += suffix;
            return res;
        }

        // xxx->xxx.compile_err
        static std::string getCompileErr(const std::string &file_name)
        {
            return addSuffix(file_name, ".compile_err");
        }

        // xxx->xxx.exe
        static std::string getExe(const std::string &file_name)
        {
            return addSuffix(file_name, ".exe");
        }

        // xxx->xxx.cpp
        static std::string getSrc(const std::string &file_name)
        {
            return addSuffix(file_name, ".cpp");
        }

        // xxx->xxx.rin
        static std::string getRunIn(const std::string &file_name)
        {
            return addSuffix(file_name, ".rin");
        }

        // xxx->xxx.rout
        static std::string getRunOut(const std::string &file_name)
        {
            return addSuffix(file_name, ".rout");
        }

        // xxx->xxx.rerr
        static std::string getRunErr(const std::string &file_name)
        {
            return addSuffix(file_name, ".rerr");
        }
    };
    const std::string PathUtil::tempdir_path("./tempdir/");

    class StringUtil
    {
    public:
        // 传入: abc{}xyz {} 123
        // 替换: abc{}xyz -> abc123xyz
        // 如果成功则返回替换字符串的下一个位置,以上面为例返回x的下标
        // 如果失败则返回std::string::npos
        static size_t oneReplace(std::string &result, const std::string &placeholder, const std::string &replacement, size_t pos = 0)
        {
            size_t index = result.find(placeholder, pos);
            if (index != std::string::npos)
            {
                result.replace(index, placeholder.size(), replacement);
                return index + replacement.size();
            }
            else
            {
                return index;
            }
        }

        static int cutString(const std::string &str, const std::string &sep, std::vector<std::string> &tokens)
        {
            size_t start_pos = 0;
            int count=0;
            // 开始位置必须在str下标的合理区间
            while (start_pos<str.size())
            {   
                count++;
                size_t find_pos = str.find(sep, start_pos);
                if (find_pos != std::string::npos) 
                {
                    tokens.push_back(str.substr(start_pos, find_pos - start_pos));
                    start_pos = find_pos + sep.size();
                }
                else
                {
                    tokens.push_back(str.substr(start_pos));
                    break;
                }
            }

            return count;
        }
    };

    class ProcessUtil
    {
    public:
        static void setCpuMemLimit(int cpu_limit /*s*/, int mem_limit /*KB*/)
        {
            struct rlimit rcpu;
            rcpu.rlim_cur = cpu_limit;
            rcpu.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_CPU, &rcpu);

            struct rlimit rmem;
            rmem.rlim_cur = mem_limit * 1024;
            rmem.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_AS, &rmem);
        }
    };
}

#endif // __UTIL_HPP__